package com.songlanyun.modules.integralrecord.service.impl;

import cn.hutool.core.map.MapUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.songlanyun.common.enums.BillConstant;
import com.songlanyun.common.enums.IntegralConstant;
import com.songlanyun.common.enums.LockPrefixConstant;
import com.songlanyun.common.enums.OpusConstant;
import com.songlanyun.common.utils.PageUtils;
import com.songlanyun.common.utils.Query;
import com.songlanyun.common.utils.custom.MathUtils;
import com.songlanyun.modules.account.service.UserIdentityAssetService;
import com.songlanyun.modules.account.service.UserInfoService;
import com.songlanyun.modules.integralconfig.entity.IntegralConfig;
import com.songlanyun.modules.integralconfig.service.IntegralConfigService;
import com.songlanyun.modules.integralrecord.dao.IntegralRecordDao;
import com.songlanyun.modules.integralrecord.entity.IntegralRecord;
import com.songlanyun.modules.integralrecord.service.IntegralRecordService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Map;
import java.util.concurrent.TimeUnit;


@Service("integralRecordService")
public class IntegralRecordServiceImpl extends ServiceImpl<IntegralRecordDao, IntegralRecord> implements IntegralRecordService {

    @Resource
    private UserInfoService userInfoService;
    @Resource
    private UserIdentityAssetService userIdentityAssetService;
    @Resource
    private IntegralConfigService integralConfigService;
    @Resource
    private RedissonClient redissonClient;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        Long userId = MapUtil.getLong(params, "userId");
        Integer billType = MapUtil.getInt(params, "bill_type");

        IPage<IntegralRecord> page = this.page(
                new Query<IntegralRecord>().getPage(params),
                new LambdaQueryWrapper<IntegralRecord>()
                        .eq(userId != null, IntegralRecord::getUserId, userId)
                        .eq(billType != null, IntegralRecord::getBillType, billType)
                        .orderByDesc(IntegralRecord::getCreateTime)
        );

        return new PageUtils(page);
    }

    @Override
    public void outer(Long userId, BigDecimal value, IntegralConstant.OuterMode outerMode, IntegralConstant.IntegralModelType modelType, Long modelId, String remark) {
        if (value.compareTo(BigDecimal.ZERO) <= 0) {//积分为0时，无需进行操作
            return;
        }

        RLock lock = redissonClient.getLock(LockPrefixConstant.USER_INTEGRAL + userId);
        lock.lock(2, TimeUnit.SECONDS);

        try {
            userIdentityAssetService.updateIntegral(userId, value, false);
        } finally {
            lock.unlock();
        }

        //保存支出积分记录
        saveOuterRecord(userId, value, outerMode, modelType, modelId, remark);
    }

    @Override
    public void outer(IntegralRecord record) {
        if (record.getValue().compareTo(BigDecimal.ZERO) <= 0) {//积分为0时，无需进行操作
            return;
        }

        RLock lock = redissonClient.getLock(LockPrefixConstant.USER_INTEGRAL + record.getUserId());
        lock.lock(2, TimeUnit.SECONDS);

        try {
            userIdentityAssetService.updateIntegral(record.getUserId(), record.getValue(), false);
        } finally {
            lock.unlock();
        }

        //保存支出积分记录
        record.setBillType(BillConstant.BillMode.EXPENDITURE);
        this.baseMapper.insert(record);

    }


    @Override
    public void inner(IntegralRecord record) {
        if (record.getValue().compareTo(BigDecimal.ZERO) <= 0) {//积分为0时，无需进行操作
            return;
        }

        RLock lock = redissonClient.getLock(LockPrefixConstant.USER_INTEGRAL + record.getUserId());
        lock.lock(2, TimeUnit.SECONDS);

        BigDecimal newValue = BigDecimal.ZERO;
        try {
            //改变会员积分 //收入
            //判断获得积分是否超过配置中的值,超过则重置积分
            newValue = record.getValue();//verifyIntegral(record.getValue(), record.getUserId(), record.getInnerMode(), record.getModelType());
            if (newValue.compareTo(BigDecimal.ZERO) > 0) {//有积分时，更新积分
                userIdentityAssetService.updateIntegral(record.getUserId(), newValue, true);
            }

        } finally {
            lock.unlock();
        }

        if (newValue.compareTo(BigDecimal.ZERO) > 0) {
            //保存积分记录
            record.setBillType(BillConstant.BillMode.INCOME);
            record.setValue(newValue);
            this.baseMapper.insert(record);
        }
    }

    private void saveInnerRecord(Long userId, BigDecimal value, IntegralConstant.InnerMode innerMode, IntegralConstant.IntegralModelType modelType, Long modelId, String remark) {
        IntegralRecord record = new IntegralRecord();
        record.setUserId(userId);
        record.setValue(value);
        record.setBillType(BillConstant.BillMode.INCOME);
        record.setInnerMode(innerMode);
        record.setModelType(modelType);
        record.setModelId(modelId);
        record.setRemark(remark);
        this.baseMapper.insert(record);
    }


    private void saveOuterRecord(Long userId, BigDecimal value, IntegralConstant.OuterMode outerMode, IntegralConstant.IntegralModelType modelType, Long modelId, String remark) {
        IntegralRecord record = new IntegralRecord();
        record.setUserId(userId);
        record.setValue(value);
        record.setBillType(BillConstant.BillMode.EXPENDITURE);
        record.setOuterMode(outerMode);
        record.setModelType(modelType);
        record.setModelId(modelId);
        record.setRemark(remark);
        this.baseMapper.insert(record);
    }


    @Override
    public Boolean exists(OpusConstant.ModelType opus, Long modelId, Long userId, IntegralConstant.InnerMode mode) {
        Integer count = this.baseMapper.selectCount(
                new LambdaQueryWrapper<IntegralRecord>()
                        .eq(IntegralRecord::getUserId, userId)
                        .eq(IntegralRecord::getBillType, BillConstant.BillMode.INCOME)
                        .eq(IntegralRecord::getInnerMode, mode)
                        .eq(IntegralRecord::getModelType, opus)
                        .eq(IntegralRecord::getModelId, modelId)
        );

        return count > 0;
    }

    private BigDecimal verifyIntegral(BigDecimal integral, Long userId, IntegralConstant.InnerMode innerMode, IntegralConstant.IntegralModelType modelType) {

        //该用户在该类型下获得的总积分
        BigDecimal sum = this.baseMapper.sum(userId, innerMode.getCode(), modelType.getCode());

        //查询配置
        IntegralConfig config = integralConfigService.get();
        BigDecimal oneIntegral = BigDecimal.ZERO; //一次获得多少积分
        BigDecimal upperLimit = null;//上限多少积分

        switch (innerMode) {
            case BY_BROWSE://被浏览
                oneIntegral = config.getByBrowse();
                upperLimit = config.getByBrowseLimit();
                break;
            case BY_LIKE://被点赞
                oneIntegral = config.getByLike();
                upperLimit = config.getByLikeLimit();
                break;
            case BY_COMMENT://被评论
                oneIntegral = config.getByComment();
                upperLimit = config.getByCommentLimit();
                break;
            case BY_SHARE://被分享
                oneIntegral = config.getByShare();
                upperLimit = config.getByShareLimit();
                break;
            case BROWSE://浏览
                oneIntegral = config.getBrowse();
                upperLimit = config.getBrowseLimit();
                break;
            case LIKE://点赞
                oneIntegral = config.getLike();
                upperLimit = config.getLikeLimit();
                break;
            case COMMENT://评论
                oneIntegral = config.getComment();
                upperLimit = config.getCommentLimit();
                break;
            case SHARE://分享
                oneIntegral = config.getShare();
                upperLimit = config.getShareLimit();
                break;
            default:
                oneIntegral = integral;
                break;
        }

        BigDecimal value = oneIntegral; //所获得的积分
        //如果之前的总积分加上此次的积分大于上限，则重新设置获得的积分
        if (upperLimit != null && (MathUtils.add(sum, oneIntegral)).compareTo(upperLimit) > 0) {
//            value = oneIntegral - ((sum + oneIntegral) - upperLimit);
            value = MathUtils.subtract(oneIntegral, (MathUtils.subtract((MathUtils.add(sum, oneIntegral)), upperLimit)));
        }

        return value;

    }


}
